#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h> 
#include <pthread.h>
#include <mqueue.h>

#include "config.h"
#include "lecture_windows.h"

#define BUFFER_SIZE 256


// ------------------- SCRIPT INFO -------------------------------
// This thread creates a TCP Internet socket port 5001 that accept a client connexion.
// It's the client which connects to the socket.
// We will use this socket to read data arriving from the client application e.g. taking decisions
//
// Running loop: we wait for data sent from operator by the socket, parse it and relieve it
// to the concerned destination



void* LectureWindows()
{
	
// ------------------------------------------------------------------
// 			1. INITIALISATION				
// ------------------------------------------------------------------

	// -------------- LINUX APPLICATION INTERFACE --------------//

	// any msg that we send doesn't have any special priority
	unsigned int msgPrio = 1;

	mqd_t queue_demarrage;
	mqd_t queue_versDestockage;
	mqd_t queue_versArchivage;
	mqd_t queue_decisionErreur;

	MSG_demarrage msg_demarrage;
	MSG_versDestockage msg_versDestockage;
	MSG_versArchivage msg_versArchivage;
	MSG_decisionErreur msg_decision;

	// opening all BAL, WRITE only
	queue_demarrage =  mq_open(BAL_demarrage, O_WRONLY);
	queue_versDestockage =  mq_open(BAL_versDestockage, O_WRONLY);
	queue_versArchivage =  mq_open(BAL_versArchivage, O_WRONLY);
	queue_decisionErreur =  mq_open(BAL_decisionErreur, O_WRONLY);

	


	// ------------- NETWORK INTERFACE ------------------------//

	int listenfd = 0, connfd = 0;
	struct sockaddr_in serv_addr; 

	char readingBuf[BUFFER_SIZE];

	listenfd = socket(AF_INET, SOCK_STREAM, 0); //socket INTERNET over TCP
	memset(&serv_addr, 0, sizeof(serv_addr)); // initialisation
	memset(readingBuf, 0, BUFFER_SIZE); // initialisation

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(5000); //set the port to 5000

	bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 

 	listen(listenfd, 10);  //listen up to then client connecting

	connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); //accept a new connection 




// -------------------------------------------------------------------
// 			2. RUNNING LOOP
// -------------------------------------------------------------------
	
	for(;;)
	{
		// waiting for a client msg
        	while(read(connfd,readingBuf,sizeof(readingBuf)) == 0); 
		#ifdef DEBUG
		printf("[LectureWindows] Received socket msg\n");
		#endif
		
		if(readingBuf[0] == '1') // type == 1, OF
		{
			// Nb batch -------------------
			char s_nbBatch[6];
			int i;
			for(i=0; i<5; i++)
			{
				s_nbBatch[i] = readingBuf[i+1];
			}
			int nbBatch = atoi(s_nbBatch);

			// Nb pallets A -------------------
			char s_nbPal_A[4];
			for(i=0; i<3; i++)
			{
				s_nbPal_A[i] = readingBuf[i+6];
			}
			int nbPal_A = atoi(s_nbPal_A);

			// Nb box / pallet A ---------------
			char s_nbBox_A[4];
			for(i=0; i<3; i++)
			{
				s_nbBox_A[i] = readingBuf[i+9];
			}
			int nbBox_A = atoi(s_nbBox_A);

			// Nb products / box A -------------
			char s_nbProd_A[3]; 
			for(i=0; i<2; i++)
			{
				s_nbProd_A[i] = readingBuf[i+12];
			}
			int nbProd_A = atoi(s_nbProd_A);

			// Nb pallets B --------------------
			char s_nbPal_B[4];
			for(i=0; i<3; i++)
			{
				s_nbPal_B[i] = readingBuf[i+14];
			}
			int nbPal_B = atoi(s_nbPal_B);

			// Nb box / pallet B ----------------
			char s_nbBox_B[4];
			for(i=0; i<3; i++)
			{
				s_nbBox_B[i] = readingBuf[i+17];
			}
			int nbBox_B = atoi(s_nbBox_B);

			// Nb products / box B --------------
			char s_nbProd_B[3];
			for(i=0; i<2; i++)
			{
				s_nbProd_B[i] = readingBuf[i+20];
			}
			int nbProd_B = atoi(s_nbProd_B);

			
			// Writing order in shared memory
			pthread_mutex_lock(MutexSHMOF);
			OrdreFabrication->numLot 		= nbBatch;
			OrdreFabrication->nbPaletteA 		= nbPal_A;
			OrdreFabrication->taillePaletteA	= nbBox_A;
			OrdreFabrication->tailleCartonA 	= nbProd_A;
			OrdreFabrication->nbPaletteB 		= nbPal_B;
			OrdreFabrication->taillePaletteB	= nbBox_B;
			OrdreFabrication->tailleCartonB 	= nbProd_B;
			pthread_mutex_unlock(MutexSHMOF);		
			
			
			// Eventually, notify Conditionnement to launch production
			msg_demarrage.msg = 0;
			if (0 != mq_send(queue_demarrage, (const char *)&msg_demarrage, sizeof(MSG_demarrage), msgPrio))
			{
				perror ("[LectureWindows] Failed to Send BAL_demarrage:");
			}

			// and notify Archivage
			msg_versArchivage.source = T_LECTURE;
			msg_versArchivage.message = LANCEMENT_OF;
			msg_versArchivage.heure = time(NULL);	
			msg_versArchivage.numLot = nbBatch;
			msg_versArchivage.cartonEnCours = 0;
			msg_versArchivage.type = ARCHIVAGE;

			if (0 != mq_send(queue_versArchivage, (const char*) &msg_versArchivage, sizeof(MSG_versArchivage), msgPrio))
			{
				perror ("[LectureWindows] Failed to Send BAL_versArchivage:");
			}
		}
		else if(readingBuf[0] == '2') // type == 2, order
		{
			char s_nbPal_A[4];
			int i;
			for(i=0; i<3; i++)
			{
				s_nbPal_A[i] = readingBuf[i+1];
			}
			int nbPal_A = atoi(s_nbPal_A);

			char s_nbPal_B[4];
			for(i=0; i<3; i++)
			{
				s_nbPal_B[i] = readingBuf[i+4];
			}
			int nbPal_B = atoi(s_nbPal_B);

			// Reading stock in shared memory
			pthread_mutex_lock(MutexSHMStock);
			int stockA = ZoneStockage->nbPaletteA;
			int stockB = ZoneStockage->nbPaletteB;
			pthread_mutex_unlock(MutexSHMStock);

			if( (nbPal_A <= stockA) && (nbPal_B <= stockB) ) 
			{ // order possible, notify Destockage
				msg_versDestockage.nbPaletteA = nbPal_A;
				msg_versDestockage.nbPaletteB = nbPal_B;

				if (0 != mq_send(queue_versDestockage, (const char *)&msg_versDestockage, sizeof(MSG_versDestockage), msgPrio))
				{
					perror ("[LectureWindows] Failed to Send BAL_versDestockage:");
				}
			}
			else
			{ // order impossible, notify Archivage
				msg_versArchivage.source = T_LECTURE;
				msg_versArchivage.message = CMD_IMPOSSIBLE;
				msg_versArchivage.heure = time(NULL);	
				msg_versArchivage.numLot = 0;
				msg_versArchivage.cartonEnCours = 0;
				msg_versArchivage.type = ARCHIVAGE;

				if (0 != mq_send(queue_versArchivage, (const char*) &msg_versArchivage, sizeof(MSG_versArchivage), msgPrio))
				{
					perror ("[LectureWindows] Failed to Send BAL_versArchivage:");
				}
			}
		}
		else if(readingBuf[0] == '3') // type == 3, operator decision
		{
			
			int reponse = atoi(&readingBuf[1]);
			if(reponse == 0){
				msg_decision.reponse = FIN_APPLI;
			}
			else{
				msg_decision.reponse = REPRISE;
			}
			if (0 != mq_send(queue_decisionErreur, (const char *)&msg_decision, sizeof(MSG_decisionErreur), msgPrio))
			{
				perror ("[LectureWindows] Failed to Send BAL_decisionErreur:");
			}
		}
		else if(readingBuf[0] == '4') // type == 4, application End
		{
			#ifdef DEBUG
			printf("[LectureWindows] Received order to end application\n");
			#endif
			
			// notify Destockage to finish
			msg_versDestockage.nbPaletteA = -1;
			msg_versDestockage.nbPaletteB = -1;
			if (0 != mq_send(queue_versDestockage, (const char *)&msg_versDestockage, sizeof(MSG_versDestockage), msgPrio))
			{
				perror ("[LectureWindows] Failed to Send BAL_versDestockage:");
			}

			// notify Archivage to finish
			msg_versArchivage.source = T_LECTURE;
			msg_versArchivage.message = CLOSE_APPLI;
			msg_versArchivage.heure = time(NULL);	
			msg_versArchivage.numLot = 0;
			msg_versArchivage.cartonEnCours = 0;
			msg_versArchivage.type = ARCHIVAGE;
			if (0 != mq_send(queue_versArchivage, (const char*) &msg_versArchivage, sizeof(MSG_versArchivage), msgPrio))
			{
				perror ("[LectureWindows] Failed to Send BAL_versArchivage:");
			}

			break; // stop listening
		}
		else // unexpected. Error
		{
			#ifdef DEBUG
			printf("[LectureWindows] Received unknown data.\n");
			#endif
		}


	} // end for

        close(connfd);
	
	mq_close(queue_demarrage);
	mq_close(queue_versDestockage);
	mq_close(queue_versArchivage);
	mq_close(queue_decisionErreur);

	// exit
	pthread_exit(0);

} //end thread

